// CS U213 Spring 2009
// moon-walker.java
// A simple example of the World game

import draw.*;
import geometry.*;
import colors.*;

/*
+---------------+
| MoonWalk      |
+---------------+
| Crater crater |
| Walker walker |
+---------------+

*/


// to represent a Moon walker world
class MoonWalk extends World{
               
  Crater crater;
  Walker walker;

  MoonWalk(Crater crater, Walker walker) {
    this.crater = crater;
    this.walker = walker;
  }
  
  // respond to key event by changing the direction of the walker
  World onKeyEvent(String ke){
    return new MoonWalk(this.crater, this.walker.onKeyEvent(ke));
  }
  
  // on tick move the walker in the current direction 
  // and update time for the crater
  World onTick(){
    if (this.crater.eatsWalker(this.walker)){
      return endOfWorld("Walker fell into the crater");
    } else {
      return new MoonWalk(this.crater.onTick(), this.walker.onTick());
    }
  }
  
  // draw this world
  boolean draw(){
    return this.crater.drawCrater(this.theCanvas) &&
           this.walker.drawWalker(this.theCanvas);
  }
  
  // start the world and the timer
  boolean go() { return this.bigBang(200, 200, 0.1); }

}


/*
+----------+
| Crater   |
+----------+
| Posn loc |
| int time |
+----------+

*/

// to represent a Moon crater
class Crater {
  Posn loc;
  int time;

  Crater(Posn loc, int time) {
    this.loc = loc;
    this.time = time;
  }
  
  // the crater disappears once the time runs out
  Crater onTick(){
    if (this.time > 0){
      return new Crater(this.loc, this.time - 1);
    } else{
      return this;
    }
  }
  
  // did this grater eat the given walker?
  boolean eatsWalker(Walker w){
    return Math.abs(this.loc.x - w.pos.x) + 
           Math.abs(this.loc.y - w.pos.y) < 10;
  }
  
  // draw this crater on the given Canvas
  boolean drawCrater(Canvas c){
    if (time > 0){
      return c.drawDisk(loc, 10, new Black());
    } else{
      return c.drawDisk(loc, 1, new Black());
    }
  }  
}

/*
+----------+
| Walker   |
+----------+
| Posn pos |
| int dir  |
+----------+

*/

// to represent a Moon walker
class Walker {
  Posn pos;
  int leftRight;
  int upDown;
  
  int UP = -3;
  int DOWN = 3;
  int LEFT = -3;
  int RIGHT = 3;
  int STOP = 0;

  Walker(Posn pos, int leftRight, int upDown) {
    this.pos = pos;
    this.leftRight = leftRight;
    this.upDown = upDown;
  }
  
  // produce a walker heading in the direction given by the key
  Walker onKeyEvent(String ke){
    if (ke.equals("up")){
      return new Walker(this.pos, this.STOP, this.UP); 
    } else {if (ke.equals("down")){
      return new Walker(this.pos, this.STOP, this.DOWN); 
    } else {if (ke.equals("left")){
      return new Walker(this.pos, this.LEFT, this.STOP); 
    } else {if (ke.equals("right")){
      return new Walker(this.pos, this.RIGHT, this.STOP); 
    } else{
      return this;      
    }}}}
  }
  
  // produce a walker that moved one step on tick
  Walker onTick(){
    return 
      new Walker(new Posn(this.pos.x + this.leftRight, 
                          this.pos.y + this.upDown), 
                 this.leftRight, this.upDown); 
  }
  
  // draw this walker on the given canvas
  boolean drawWalker(Canvas c){
    return c.drawDisk(this.pos, 5, new Red()); 
  }
}


class Examples{
  Examples(){}
  
  int UP = -3;
  int DOWN = 3;
  int LEFT = -3;
  int RIGHT = 3;
  int STOP = 0;
  
  Crater cr1 = new Crater(new Posn(20, 30), 15);
  Crater crGone = new Crater(new Posn(80, 30), 0);
  Walker neal = new Walker(new Posn(100, 100), this.STOP, this.UP);
  Walker nealMoved = new Walker(new Posn(100, 97), this.STOP, this.UP);
  Walker nealDown = new Walker(new Posn(100, 100), this.STOP, this.DOWN);
  Walker nealLeft = new Walker(new Posn(100, 100), this.LEFT, this.STOP);
  Walker nealLeftMoved = new Walker(new Posn(97, 100), this.LEFT, this.STOP);
  Walker nealRight = new Walker(new Posn(100, 100), this.RIGHT, this.STOP);
  Walker nemo = new Walker(new Posn(22, 28), this.DOWN, this.STOP);
  
  Canvas c = new Canvas(400, 400);
  
  // test the method onTick in the class Crater
  boolean testOnTick(){
    return (check this.cr1.onTick() expect new Crater(new Posn(20, 30), 14)) &&
           (check this.crGone.onTick() expect this.crGone);
  }

  // test the method eatsWalker in the class Crater
  boolean testEatsWalker(){
    return (check cr1.eatsWalker(neal) expect false) &&
           (check cr1.eatsWalker(nemo) expect true);
  }
  
  // visual test for drawing a crater
  boolean drawCrater(){
    return cr1.drawCrater(this.c) && crGone.drawCrater(this.c);
  }
  
  // test the method onKeyEvent in the class Walker
  boolean testOnKeyEvent(){
    return (check this.nealLeft.onKeyEvent("up") expect this.neal) &&
           (check this.neal.onKeyEvent("up") expect this.neal) &&
           (check this.nealLeft.onKeyEvent("down") expect this.nealDown) &&
           (check this.nealLeft.onKeyEvent("right") expect this.nealRight) &&
           (check this.nealLeft.onKeyEvent("x") expect this.nealLeft) &&
           (check this.nealDown.onKeyEvent("left") expect this.nealLeft);
  }
  
  // test the method onTick in the class Walker
  boolean testOnTickWalker(){
    return (check this.neal.onTick() expect this.nealMoved) &&
           (check this.nealLeft.onTick() expect this.nealLeftMoved);           
  }

  // a visual test for drawing a walker
  boolean drawWalker(){
    return this.neal.drawWalker(this.c) &&
           this.nemo.drawWalker(this.c); 
  }

  boolean drawAll(){
    return this.c.show() &&
           this.drawCrater() &&
           this.drawWalker(); 
  }
  
  MoonWalk mw = new MoonWalk(this.cr1, this.neal);
  
  /*
    To run the program type in the Interactions: 
     Examples e = new Examples();
     e.mw.bigBang(400, 400, 0.1);
  */
   
}
  
